# -*-coding:utf-8-*-

import functools

from bson.objectid import ObjectId
from tornado.gen import coroutine

from tornado.ioloop import PeriodicCallback
import tornado

from pyrestful import mediatypes
from pyrestful.rest import get, post, put, delete
from handlers.base import BaseHandler
from utils.auth import authenticated


# NOTE:
# pyrestful bug:
# GET /api/orders/{oid} == GET /platform/api/orders
# {oid} = platform
# 临时解决方案：将 platform 与非 platform 接口分放至不同类


class PlatformOrdersHandler(BaseHandler):
    def __init__(self, application, request, **kwargs):
        super(PlatformOrdersHandler, self).__init__(application, request, **kwargs)
        self.delegate_name = 'orders'
        self.status = ['delivering', 'delivered', 'notify_return', 'refuse', 'returned',
                       'to_be_paid', 'paid', 'done', 'apply_return', 'returning']

    def check_permissions(self, permissions):
        return True

    @put(_path='/platform/api/orders/{oid}/status', _types=[str, dict],
         _consumes=mediatypes.APPLICATION_JSON)
    @authenticated
    @coroutine
    def update_order_status(self, oid, request):
        status = request['status']
        if status not in self.status:
            raise Exception('supported status are: %s' % str(self.status))
        kwargs = dict(oid=oid, modification=request)
        handle_update = functools.partial(self.orders_dao.update_order_status, oid)
        result = yield self.delegate_update(self.delegate_name, handle_update, request, **kwargs)
        self.respond_success(result)

    @post(_path='/platform/api/orders', _types=[dict],
          _consumes=mediatypes.APPLICATION_JSON)
    @authenticated
    @coroutine
    def create_order(self, request):
        products = []
        total_price = 0.0
        order_type = None
        for product in request['products']:
            pid = product['product_id']
            p = yield self.products_dao.get_record_by_id(pid)
            if p is None:
                raise Exception('invalid product id(%s)' % pid)
            unit_price = p.unit_price
            price = unit_price * product['quantity']
            total_price += price
            required_fields = ['product_id', 'quantity']
            optional_fields = ['specifics']
            product = self.extract_fields(product, required_fields=required_fields, optional_fields=optional_fields)
            product['type'] = p.type if p.type is not None else 'goods'
            if order_type is None:
                order_type = product['type']
            elif order_type != product['type']:
                raise Exception('different products must not be mixed together in one order')
            if 'service_one_shot' == order_type:
                result = yield self.orders_dao.count({'uid': self.user_id, 'products.product_id': pid})
                if 0 < result:
                    raise Exception('on shot service(%s) have already been consumed' % p.name)
            product['unit_price'] = unit_price  # NOTE: 以服务端的单价为准
            products.append(product)
        kwargs = {
            'products': products,
            'total_price': total_price,
        }
        specifics = yield self.delegate_pre_create(self.delegate_name, **kwargs)
        if specifics is not None:
            request['specifics'] = specifics
        request['total_price'] = total_price
        request['type'] = order_type
        result = yield self.orders_dao.create_order(self.user_id, request)
        self.respond_success(result)

    @get(_path='/platform/api/orders')
    @authenticated
    @coroutine
    def get_order_list(self):
        result = yield self.delegate_get_list(self.orders_dao, 'order_id')
        self.respond_success(result)

    @get(_path='/platform/api/orders/{aspect}', _types=[str])
    @authenticated
    @coroutine
    def get_order_aspect(self, aspect):
        if 'count' == aspect:
            result = yield self.delegate_get_list(self.orders_dao, 'order_id', get_count=True)
        elif 'statistics' == aspect:
            kwargs = self.query_arguments()
            pipeline, default = yield self.delegate_pre_get_statistics(self.delegate_name, **kwargs)
            cursor = self.orders_dao.aggregate(pipeline)
            yield cursor.fetch_next
            result = cursor.next_object()
            if result is None:
                result = default
            else:
                result.pop('_id')
            # # 简单的通过遍历查询结果方式进行聚合运算
            # kwargs = self.query_arguments()
            # delegate = self.get_delegate(self.delegate_name)
            # result = delegate.on_pre_traverse(**kwargs)
            # if isinstance(result, tornado.concurrent.Future):
            #     result = yield result
            # filter_cond, projection = result
            # cursor = self.orders_dao.find(filter_cond, projection)
            # while (yield cursor.fetch_next):
            #     r = cursor.next_object()
            #     result = delegate.on_traversing_item(r, **kwargs)
            #     if isinstance(result, tornado.concurrent.Future):
            #         yield result
            # result = delegate.on_post_traverse(**kwargs)
            # if isinstance(result, tornado.concurrent.Future):
            #     result = yield result
                #     result.append(self.transfers_dao.get_transferred_amount(self.user_id))
                #     result[4] -= result[5]
        else:
            oid = aspect
            kwargs = self.query_arguments()
            filter_cond, projection = yield self.delegate_pre_get(self.delegate_name, **kwargs)
            filter_cond.update(_id=ObjectId(oid))
            result = yield self.orders_dao.find_one(filter_cond, projection)
        self.respond_success(result)

    @delete(_path='/platform/api/orders/{oid}', _types=[str])
    @authenticated
    @coroutine
    def delete_order(self, oid):
        order = yield self.orders_dao.get_record_by_id(oid)
        if order is None:
            raise Exception('invalid order id')
        if self.prod and 'to_be_paid' != order.status:
            raise Exception('invalid order status(%s)' % order.status)
        if self.prod:
            result = yield self.orders_dao.delete_order(self.user_id, oid)
        else:
            result = yield self.orders_dao.test_delete_order(self.user_id, oid)
        self.respond_success(result)
